package io.renren.modules.minio.service.impl;

import cn.hutool.core.io.FileMagicNumber;
import cn.hutool.core.io.FileTypeUtil;
import cn.hutool.core.io.file.FileNameUtil;
import cn.hutool.core.util.IdUtil;
import io.renren.common.exception.ErrorCode;
import io.renren.common.exception.RenException;
import io.renren.common.utils.DateUtils;
import io.renren.modules.minio.service.MinioService;
import io.renren.modules.minio.util.MinioFileUtil;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import net.coobird.thumbnailator.Thumbnails;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.util.*;

/**
 * minio
 *
 * @author 40659
 * @since 2025/1/22 09:07
 */
@Slf4j
@Service
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class MinioServiceImpl implements MinioService {

    private final MinioFileUtil minioFileUtil;

    /**
     * 文件删除
     *
     * @param filePath 文件路径
     */
    @Override
    public Boolean deleteFile(List<String> filePath) {
        try {
            return minioFileUtil.deleteFile(filePath);
        } catch (Exception e) {
            log.error("文件删除失败", e);
            return false;
        }
    }

    @Override
    public InputStream getInputStream(String filePath) {
        try {
            return minioFileUtil.getInputStream(filePath);
        } catch (Exception e) {
            log.error("获取文件流失败", e);
        }
        return null;
    }

    private String upload(InputStream is, String suffix, String contentType) throws Exception {
        String uuid = IdUtil.simpleUUID();
        String yyyyMm = DateUtils.format(new Date(), "yyyyMM");

        String filePath = yyyyMm + "/" + uuid + "." + suffix;

        //把文件放置Minio桶(文件夹)
        minioFileUtil.uploadFile(filePath, is, contentType);
        return filePath;
    }

    /**
     * 判断是否是视频格式
     *
     * @param fileType 文件类型
     * @return true or fasle
     */
    private boolean isVideoFile(String fileType) {
        return FileMagicNumber.MP4.getExtension().equals(fileType) || FileMagicNumber.AVI.getExtension().equals(fileType)
                || FileMagicNumber.WMV.getExtension().equals(fileType) || FileMagicNumber.M4V.getExtension().equals(fileType)
                || FileMagicNumber.FLV.getExtension().equals(fileType) || FileMagicNumber.MKV.getExtension().equals(fileType)
                || FileMagicNumber.WEBM.getExtension().equals(fileType) || FileMagicNumber.MOV.getExtension().equals(fileType)
                || FileMagicNumber.MPEG.getExtension().equals(fileType) || FileMagicNumber.RMVB.getExtension().equals(fileType)
                || FileMagicNumber.M3GP.getExtension().equals(fileType);
    }

    /**
     * 判断是否是图片格式
     *
     * @param fileType 文件类型
     * @return true or fasle
     */
    private boolean isImageFile(String fileType) {
        return FileMagicNumber.JPEG.getExtension().equals(fileType) || FileMagicNumber.APNG.getExtension().equals(fileType)
                || FileMagicNumber.PNG.getExtension().equals(fileType) || FileMagicNumber.GIF.getExtension().equals(fileType)
                || FileMagicNumber.BMP.getExtension().equals(fileType) || FileMagicNumber.TIFF.getExtension().equals(fileType)
                || FileMagicNumber.WEBP.getExtension().equals(fileType) || FileMagicNumber.ICO.getExtension().equals(fileType);
    }

    /**
     * 判断是否是文档格式
     *
     * @param fileType 文件类型
     * @return true or fasle
     */
    private boolean isDocumentFile(String fileType, String suffix) {
        if (StringUtils.hasText(fileType)) {
            if (FileMagicNumber.PDF.getExtension().equals(fileType) || FileMagicNumber.DOC.getExtension().equals(fileType)
                    || FileMagicNumber.DOCX.getExtension().equals(fileType) || FileMagicNumber.XLS.getExtension().equals(fileType)
                    || FileMagicNumber.XLSX.getExtension().equals(fileType)) {
                return true;
            } else if (FileMagicNumber.ZIP.getExtension().equals(fileType) &&
                    (suffix.equalsIgnoreCase("doc") || suffix.equalsIgnoreCase("docx") ||
                            suffix.equalsIgnoreCase("xls") || suffix.equalsIgnoreCase("xlsx"))) {
                return true;
            }
        } else {
            if (suffix.equalsIgnoreCase("doc") || suffix.equalsIgnoreCase("docx") ||
                    suffix.equalsIgnoreCase("xls") || suffix.equalsIgnoreCase("xlsx")) {
                return true;
            }
        }
        return false;
    }

    @Override
    public String upload(MultipartFile file) {
        if (file.isEmpty()) {
            throw new RenException(ErrorCode.UPLOAD_FILE_EMPTY);
        }
        try (InputStream is = file.getInputStream()) {
            String contentType = file.getContentType();
            String fileType = FileTypeUtil.getType(is);
            //获取文件的后缀名
            String suffix = FileNameUtil.getSuffix(file.getOriginalFilename());
            if (!isImageFile(fileType) && !isVideoFile(fileType) && !isDocumentFile(fileType, suffix)) {
                throw new RenException("不支持此格式的文件上传");
            }
            long fileSize = file.getSize();

            if (fileSize > 1024 * 512 && isImageFile(fileType)) {
                // 检查文件大小是否超过1MB
                // 初始质量设为最高
                float quality = 1.0f;
                while (true) {
                    BufferedImage originalImage = ImageIO.read(file.getInputStream());
                    ByteArrayOutputStream baos = new ByteArrayOutputStream();
                    //如果图片尺寸大于1920*1920，则进行压缩
                    if (originalImage.getWidth() > 1920 || originalImage.getHeight() > 1920) {
                        Thumbnails.of(originalImage).outputQuality(quality).scale(0.5f).outputFormat("jpg").toOutputStream(baos);
                    } else {
                        Thumbnails.of(originalImage).outputQuality(quality).scale(1f).outputFormat("jpg").toOutputStream(baos);
                    }
                    byte[] compressedBytes = baos.toByteArray();
                    long compressedSize = compressedBytes.length;
                    // 如果压缩后的图片小于等于500kB，跳出循环
                    if (compressedSize <= 1024 * 512 || quality == 0.0f) {
                        file = new MockMultipartFile(
                                // 文件名，可以自定义
                                "file",
                                // 文件原始名称
                                file.getOriginalFilename(),
                                // MIME类型，例如 image/jpeg 或 image/png 等
                                suffix,
                                // 字节数组
                                compressedBytes
                        );
                        break;
                    }
                    // 减小质量，继续压缩
                    quality -= 0.1f;
                }
            }
            //得到文件流
            InputStream inputStream = file.getInputStream();

            return upload(inputStream, suffix, contentType);
        } catch (Exception e) {
            if (e instanceof RenException) {
                throw (RenException) e;
            }
            log.error("文件上传失败", e);
            throw new RenException("文件上传失败");
        }
    }

    @Override
    public String uploadBase64(String base64String) {
        if (!StringUtils.hasText(base64String)) {
            throw new RenException("文件内容为空");
        }
        byte[] bt = Base64.getDecoder().decode(base64String);
        try (InputStream is = new ByteArrayInputStream(bt)) {
            return upload(is, "png", FileMagicNumber.PNG.getMimeType());
        } catch (Exception e) {
            log.error("文件上传失败", e);
            throw new RenException("文件上传失败");
        }
    }

    @Override
    public String getUrl(String filePath) {
        return minioFileUtil.getUrl(filePath);
    }

    /**
     * 文件下载
     *
     * @param response 响应对象
     */
    @Override
    public Boolean download(String filePath, HttpServletResponse response) {
        try {
            response.setHeader("Content-Disposition", "attachment;filename=" + filePath);
            response.setContentType("application/force-download");
            response.setCharacterEncoding("UTF-8");
            minioFileUtil.writeOutputStream(filePath, response.getOutputStream());
            return true;
        } catch (Exception e) {
            log.error("文件下载失败", e);
            return false;
        }

    }

    @Override
    public void view(String filePath, HttpServletResponse response) {
        try {
            minioFileUtil.view(filePath, response.getOutputStream());
        } catch (Exception e) {
            log.error("文件预览失败", e);
        }
    }

    @Override
    public String getFileContent(String filePath) {
        try {
            return minioFileUtil.getFileContent(filePath);
        } catch (Exception e) {
            log.error("获取文件内容失败", e);
            return null;
        }
    }

    @Override
    public byte[] getFileContentByte(String filePath) {
        try {
            return minioFileUtil.getFileContentByte(filePath);
        } catch (Exception e) {
            log.error("获取文件内容失败", e);
            return null;
        }
    }
}
